﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.SecurityControl;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.DataAccess;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    /// <summary>
    /// 夜审调度
    /// <para>--------------------------</para>
    /// <para>@Author: cnalexi@gmail.com</para> 
    /// <para>@Create: 2012/5/3</para>  
    /// </summary>
    public class NightAuditDispatcher 
    {
        private static readonly IUnityContainer container = UnityContainerService.GetContainer();
        //private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>(); 

        public void Scheduling()
        {
            IHotelBizDayRepository bizDayRepository = container.Resolve<IHotelBizDayRepository>();

            //用NETBIOS NAME做夜审调度
            var hostName = System.Environment.MachineName.ToLower();

            //还要判断是否加入自动夜审
            Expression<Func<HotelBizDay, bool>> exp = r => r.HostName == hostName && r.IsValid && r.IsJoinAutoAudit;

            IList<HotelBizDay> allBizDays = bizDayRepository.BatchFind(GlobalConst.ROOT_HOTEL_ID, exp);
            
            var rootBizDay = allBizDays.FirstOrDefault(b => b.HotelID == GlobalConst.ROOT_HOTEL_ID);

            //审前检查
            var bizDays = Precheck.GetCanAuditHotels(allBizDays.Where(b => b.BizDay == rootBizDay.BizDay && b.HotelID != GlobalConst.ROOT_HOTEL_ID), rootBizDay, bizDayRepository);

            if(bizDays==null || bizDays.Count<=0)
            {
                //rootBizday翻页 并计入Log
                TurnRootBizDay(rootBizDay,bizDayRepository);

                LogMessage msg = new LogMessage(LogCategory.NaNullComplete, GlobalConst.SuperManger, rootBizDay.HostName,
                                                "", "", true, rootBizDay.HostName, "夜审完成，但没有任何酒店加入夜审，请复查！");

                OperateLog.InfoLog(msg);

                return;
            }

            //开始夜审
            StartNightAudit(bizDays,bizDayRepository); //只有单步执行夜审时需处理

            //开始监控
            var watch = System.Diagnostics.Stopwatch.StartNew();

            Task parentTask = new Task(() =>
                {
                    IList<Task> tasks = new List<Task>();

                    CancellationTokenSource cts = new CancellationTokenSource();

                    TaskFactory tf = new TaskFactory(cts.Token);

                    foreach(var bizDay in bizDays)
                    {
                        var t = tf.StartNew(() => DoAudit(bizDay));
                        try
                        {
                            tasks.Add(t);
                        }
                        catch{}
                        
                    }

                    tf.ContinueWhenAll(tasks.Where(a=>a!=null).ToArray(), result => ExcuteResult(result, bizDays,rootBizDay, watch,bizDayRepository));
                }
            );

            parentTask.Start();

            //cts.Cancel();
            
        }

        public void DoAudit(HotelBizDay bizDay)
        {
            var doNightAudit = new DoNightAudit();

            doNightAudit.Audit(bizDay);

            #region 模拟出错
            
            /*
            Random ran = new Random(DateTime.Now.Millisecond);

            int i= ran.Next(0, 5);

            if (i <=2)
            {
                try
                {
                    throw new Exception(string.Format("{0} 酒店夜审在 步骤3 出错,状态 E", bizDay.HotelID));
                
                }catch(Exception error)
                {
                    System.Diagnostics.Debug.WriteLine("异常:"+error.Message);
                    
                    bizDay.Status = EnumHelper.GetCode(NightAuditCode.Status.E);

                    bizDayRepository.Update(bizDay);
                }
            }

            System.Diagnostics.Debug.WriteLine(bizDay.HotelID+" 夜审执行中,1,2,3,4,5......");
            */
            #endregion
        }

        public void StartNightAudit(IEnumerable<HotelBizDay> bizDays,IHotelBizDayRepository bizDayRepository )
        {
            foreach(var bizDay in bizDays)
            {
                bizDay.Status = EnumHelper.GetCode(NightAuditCode.Status.S);
            }

            string hotelId= bizDays.FirstOrDefault().HotelID;

            bizDayRepository.BatchUpdate(hotelId,bizDays);
        }

        public void ExcuteResult(Task[] tasks, IList<HotelBizDay> bizDays, HotelBizDay rootBizDay, System.Diagnostics.Stopwatch watch, IHotelBizDayRepository bizDayRepository)
        {

            ISyncBizDayRepository syncRepository = container.Resolve<ISyncBizDayRepository>();

            ICacheCenterRepository cacheCenterRepository = container.Resolve<ICacheCenterRepository>();

            //查看哪些任务异常了
            var time = watch.ElapsedMilliseconds;

            var errorHotels = bizDays.Where(b => b.Status == EnumHelper.GetCode(NightAuditCode.Status.E)).ToList();

            //日志写入夜审执行了多少时间 HostName=System.Environment.MachineName.ToLower(),
            LogMessage msg = new LogMessage
                                 {
                                     CategoryID = LogCategory.NightAudit,
                                     EmployeeID="SYSTEM",
                                     HotelID="0000000",
                                     IsSuccess=true,
                                     RelatedID="0000000",
                                     OrginValue="S",
                                     DestValue="C",
                                     Message = string.Format(
                                         "共使用 {0} 毫秒，共夜审{1}家店数，夜审失败{2}家:{3}",
                                         time.ToString(),
                                         bizDays.Count(),
                                         errorHotels.Count(),
                                         string.Join(",",errorHotels))
                                 };
                                 
            System.Diagnostics.Debug.WriteLine(msg.Message);

            OperateLog.InfoLog(msg);

            TurnRootBizDay(rootBizDay,bizDayRepository);

            //同步对应hostName的营业日进入业务库
            syncRepository.Sync(bizDays);

            //刷新总营业日cache
            CacheCenter cc = new CacheCenter
                                 {
                                     CacheName=CenterCacheClass.BIZ_DAY_CACHE,
                                     HotelID=GlobalConst.ROOT_HOTEL_ID,
                                     IsValid=true,
                                     UpdateDate=DateTime.Now
                                 };

            cacheCenterRepository.Update(cc);
        }

        private void TurnRootBizDay(HotelBizDay rootBizDay, IHotelBizDayRepository bizDayRepository)
        {
            rootBizDay.BizDay = rootBizDay.BizDay.AddDays(1);

            rootBizDay.AuditTime = DateTime.Now;

            rootBizDay.Status = EnumHelper.GetCode(NightAuditCode.Status.C);

            bizDayRepository.Update(rootBizDay);

            //更新央存
            ICacheCenterRepository ccRepository = container.Resolve<ICacheCenterRepository>();

            ccRepository.UpdateCacheCenter(GlobalConst.ROOT_HOTEL_ID, GlobalConst.BIZDAY_CACHE);

        }

        
    }

}
